Using pyproject.toml in your (Django) project

您所在的位置:网站首页 pip django Using pyproject.toml in your (Django) project

Using pyproject.toml in your (Django) project

2023-03-06 11:29| 来源: 网络整理| 查看: 265

Development Django Using pyproject.toml in your (Django) project

Back in 2018, I wrote about using setup.py in your Django/Python project. Five years later, setup.py is being phased out in favor of pyproject.toml. I’m a big fan of this change. With setup.py you could really go off the rails making everything dynamic or even executing malicious code during the installation process. In contrast, pyproject.toml moves the ecosystem towards a configuration file that can be parsed without executing arbitrary code. You can read more about the rationale behind pyproject.toml in PEP-517, PEP-518, PEP-621, and PEP-660.

Creating your pyproject.toml file

If you’re using poetry, pdm, or any of the other newer Python build systems, you’re already using pyproject.toml. How about folks that are using plain old pip or pip-tools? You can still take advantage of this new file format and ditch setup.py and/or setup.cfg as well. Most third-party tooling supports configuration via the tool section defined in PEP-518.

To start, define the build system for your project. To avoid introducing new tools, we’re going to use good ol’ setuptools:

[build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta"

Next, define your project and its dependencies:

[project] name = "myproject" version = "1.0" dependencies = [ "dj-database-url", "django==3.2.*", "gunicorn", "psycopg2", "whitenoise", ] [project.optional-dependencies] dev = [ "black", "django-stubs", "ipdb", "pip-tools", "ruff", ]

At this point, you can run this to bootstrap a local development environment:

pip install --editable .[dev] Installing manage.py

In our previous post about using setup.py we showed a trick that would allow you to remove manage.py from your repo and have it installed as a “proper” script on the PATH. You can add the functionality to myproject/__init__.py like this:

import os import sys def django_manage(): os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv)

Now add this to your pyproject.toml:

[project.scripts] "manage.py" = "myproject:django_manage"

When you install your project, it will create a manage.py script on your path so you can run it like any other command, $ manage.py ....

With pip-tools dependency locking

What we have so far is all well and good, but you really should be locking/freezing your dependencies to ensure the exact same requirements are installed every time. pip-tools allows you to create these in a format where only pip is required to install them elsewhere. For your primary dependencies, you can replace where you might have previously used requirements.in with pyproject.toml:

pip-compile \ --generate-hashes \ --output-file requirements.txt \ pyproject.toml

You could do the same with your dev requirements by using the --extra dev flag:

# don't do this... see below for a better solution pip-compile \ --generate-hashes \ --output-file requirements-dev.txt \ --extra dev \ pyproject.toml

There’s an issue here, however. We want to ensure requirements-dev.txt isn’t installing packages that are incompatible with what’s in requirements.txt. pip-tools suggests a workflow for this involving the --constraint flag, but it is incompatible with pyproject.toml dependencies. Here’s an ugly workaround for this situation:

Add the --strip-extras flag when you build your requirements.txt file so it can be used as a pip constraint. Don’t worry, the same dependencies will be installed.

pip-compile \ --generate-hashes \ --output-file requirements.txt \ --strip-extras \ pyproject.toml

Pass that constraint in when you generate your dev requirements. To avoid adding more files to our project, we pass it via stdin.

echo "--constraint $(pwd)/requirements.txt" | \ pip-compile \ --generate-hashes \ --output-file requirements-dev.txt \ --extra dev \ - \ pyproject.toml

Tip: I like to hide these long commands in a Makefile or Justfile so developers only need to remember make requirements.txt.

To install dependencies from your lock file and also install your project, you can pass the --no-deps flag to pip to make sure it doesn’t try to reinstall the un-pinned dependencies in pyproject.toml:

pip install -r requirements-dev.txt pip install --no-deps -e .

It’s nice to trade setup.py, setup.cfg, requirements.in, requirements-dev.in, pytest.ini, .coveragerc, etc. with one file which defines everything Python related for your project. I hope we get the same for lock files, but with the rejection of PEP 665 we’ll have to wait a bit longer for that.

About the author

Peter Baumgartner Peter is the founder of Lincoln Loop, having built it up from a small freelance operation in 2007 to what it is today. He is constantly learning and is well-versed in many technical disciplines including devops, …

View Peter's profile

Stay updated

Get the latest development and research insights delivered to your inbox twice a month.

Related Insights Strategy How to Choose a Web Development Agency in 2023

Feb. 7, 2023

Code Python Package Manager Shootout

July 25, 2022

Django Choosing a Django Version

June 13, 2022

We built AppPack so developers can focus on code instead of infrastructure, CI pipelines, and deployments.

Visit AppPack.io



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3